/**
 * Copyright (c) 2020 lizebang
 *
 * This software is released under the MIT License.
 * https://opensource.org/licenses/MIT
 */

package jiri

import (
	"errors"
	"os"
	"strings"
	"sync"

	"go.fuchsia.dev/jiri"
	"gopkg.in/yaml.v2"

	"gitee.com/banggua/ggtsync/config"
	"gitee.com/banggua/ggtsync/logger"
)

const (
	HandlerName = "jiri"
)

type jiriClient struct {
	locker *sync.Mutex
	Xs     map[string]*jiri.X
}

var (
	DefaultJiriClient = &jiriClient{
		locker: &sync.Mutex{},
		Xs:     make(map[string]*jiri.X),
	}
)

func (jc *jiriClient) Test(project *config.Project) error {
	_, _, _, err := getArgs(project.From)
	if err != nil {
		logger.Errorf("%v", err)
		return err
	}
	if len(project.Args) != 6 {
		logger.Errorf("%v", errors.New("invalid arguments"))
		return errors.New("invalid arguments")
	}
	// 1 baseurl
	if _, exist := project.Args["baseurl"]; !exist {
		logger.Errorf("%v", errors.New("args.baseurl field does not exist"))
		return errors.New("args.baseurl field does not exist")
	}
	// 2 cipddir
	if _, exist := project.Args["cipddir"]; !exist {
		logger.Errorf("%v", errors.New("args.cipddir field does not exist"))
		return errors.New("args.cipddir field does not exist")
	}
	// 3 manifest
	if _, exist := project.Args["manifest"]; !exist {
		logger.Errorf("%v", errors.New("args.manifest field does not exist"))
		return errors.New("args.manifest field does not exist")
	}
	// 4 manifestdir
	if _, exist := project.Args["manifestdir"]; !exist {
		logger.Errorf("%v", errors.New("args.manifestdir field does not exist"))
		return errors.New("args.manifestdir field does not exist")
	}
	// 5 useremail
	if _, exist := project.Args["useremail"]; !exist {
		logger.Errorf("%v", errors.New("args.useremail field does not exist"))
		return errors.New("args.useremail field does not exist")
	}
	// 6 username
	if _, exist := project.Args["username"]; !exist {
		logger.Errorf("%v", errors.New("args.username field does not exist"))
		return errors.New("args.username field does not exist")
	}
	return nil
}

func (jc *jiriClient) Templet() string {
	var jiriYaml = &config.Project{
		Name:  "fuchsia",
		Order: 1,
		Args: map[string]string{
			"cipddir":     "/mirrors/cipd",
			"baseurl":     "https://git.fuchsia-mirror.com",
			"username":    "banggua",
			"useremail":   "banggua@qq.com",
			"manifest":    "fuchsia-integration",
			"manifestdir": "/mirrors/files",
		},
		Type:     HandlerName,
		From:     "integration flower https://fuchsia.googlesource.com/integration",
		To:       "/mirrors/git",
		Interval: 1,
		Plugins: []config.Plugin{
			{Name: "plugin1", Order: 2, Args: map[string]string{"arg1": "val1", "arg2": "val2"}},
			{Name: "plugin2", Order: 3, Args: map[string]string{"arg1": "val1", "arg2": "val2"}},
		},
	}
	out, _ := yaml.Marshal(&jiriYaml)
	return string(out)
}

func getArgs(from string) (string, string, string, error) {
	args := strings.Split(from, " ")
	if len(args) != 3 {
		logger.Errorf("%v", errors.New("from field is invalid"))
		return "", "", "", errors.New("from field is invalid")
	}
	return args[0], args[1], args[2], nil

}

func (jc *jiriClient) Handle(project *config.Project) error {
	var (
		cipddir     = project.Args["cipddir"]
		manifestdir = project.Args["manifestdir"]
	)

	err := os.MkdirAll(project.To, 0755)
	if err != nil {
		logger.Errorf("%v %v", project, err)
		return err
	}
	err = os.MkdirAll(cipddir, 0755)
	if err != nil {
		logger.Errorf("%v %v", project, err)
		return err
	}
	err = os.MkdirAll(manifestdir, 0755)
	if err != nil {
		logger.Errorf("%v %v", project, err)
		return err
	}

	// err = jc.jiriImportReplace(project)
	// if err != nil {
	// 	logger.Errorf("%v %v", project, err)
	// 	return err
	// }

	clean, err := jc.jiriInit(project)
	defer clean()
	if err != nil {
		return err
	}

	err = jc.jiriImport(project)
	if err != nil {
		return err
	}

	lock, projectOps, packageOps, err := jc.computeOps(project)
	if err != nil {
		return err
	}

	for _, op := range projectOps {
		logger.Infof("Start %#v", op)
		err = op.Run()
		if err != nil {
			logger.Errorf("%#v %v", op, err)
			return err
		}
		logger.Infof("Finish %#v", op)
	}

	for _, op := range packageOps {
		logger.Infof("Start %#v", op)
		err = op.Run()
		if err != nil {
			logger.Errorf("%#v %v", op, err)
			return err
		}
		logger.Infof("Finish %#v", op)
	}

	return lock()
}

func (jc *jiriClient) Close(project *config.Project) error {
	jc.locker.Lock()
	defer jc.locker.Unlock()
	jc.Xs[project.Name].RunCleanup()
	delete(jc.Xs, project.Name)

	return nil
}
